#include <cserver/restapi.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/log/trivial.hpp>
#include <boost/format.hpp>
#include <pqxx/transaction.hxx>

#include <sstream>

namespace csrv{ namespace webcmd{
	static const char* K_this_command= "api/v1/common/login";

	std::string makeLoginResponse(const std::string& account, const std::string& role, bool success, const std::string& message, const std::string& sessionid, const std::string& name){
		std::stringstream sstr;
		sstr << boost::format(R"({
"func":"login",
"args":{"account":%1%},
"ret":{"success":%2%, "message":%3%,
"value":{"sessionid":%4%, 
"name": %5%,"role":%6%,
"expires":""}}
})") 
			% escape_string(account) % std::string(success ? "true" : "false") % escape_string(message) % escape_string(sessionid) % escape_string(name) % escape_string(role);

			return sstr.str();

	}
	void login(Request& req, pqxx::connection& dbconn, SessionContext& sctx, const std::string& /*cmd*/ ){
		auto query = parseQuery(req.getParam("QUERY_STRING"));
		auto& callback = query["callback"];
		auto sessionid = req.getCookie("sessionid");
		{
			std::lock_guard<std::mutex> lock(sctx.mutex);
			if(sctx.activeSessions.erase(sessionid) !=0)
			{
				pqxx::work w(dbconn);
				w.prepared("delete_sessionid")(sessionid).exec();
				w.commit();
			}
		}

		StreamGroup sg(req, 4096);

		boost::property_tree::ptree ptree;
		read_json(sg.fin, ptree);
		auto node_account = ptree.get_child_optional("account");
		auto node_password = ptree.get_child_optional("password");
		std::string account;
		if (node_account && node_password){
			account = node_account.get().get_value<std::string>();
			auto password = node_password.get().get_value<std::string>();

			std::string hashed_text = hash_password(account, password);

			std::string userid, displayName, role;
			bool passed = false;
			{
				std::lock_guard<std::mutex> lock(sctx.mutex);
				auto pos = sctx.user_info.find(account);
				passed =  (pos != sctx.user_info.end() && pos->second.password == hashed_text);
				if (passed){
					userid = pos->second.userid;
					displayName = pos->second.displayName;
					role = pos->second.role;
				}
			}
			if (passed){
				pqxx::work w(dbconn);
				auto result = w.prepared("new_sessionid").exec();
				std::string sessionid = result.begin()->begin()->c_str();
				w.prepared("store_sessionid")(userid)(sessionid).exec();
				w.commit();

				sctx.activeSessions[sessionid] = account;
				response_header(sg.fout, 200, false);
				sg.fout << "Set-Cookie: sessionid=" << sessionid << "; path=/ \r\n\r\n";
				if (!callback.empty()) sg.fout << callback << "(";
				sg.fout <<  makeLoginResponse(account, role, true, "", sessionid, displayName);
				if (!callback.empty()) sg.fout << ")";


				return;
			}
		}
		response_header(sg.fout, 401);
		if (!callback.empty()) sg.fout << callback << "(";
		sg.fout <<  makeLoginResponse(account, "", false, "Failed to login", "", "");
		if (!callback.empty()) sg.fout << ")";
	}

}
static CommandAddHelper k_registerHandler("POST", webcmd::K_this_command, &webcmd::login);
}

